package test.io;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import org.apache.commons.lang3.StringUtils;


/**
 * 限制长度的ByteArrayOutputStream，超出长度的被忽略，但不报错
 * 
 * @see java.io.ByteArrayOutputStream
 * @author yuqih
 * 
 */
public class LimitedByteArrayOutputStream extends OutputStream {
	
	public static final int DEFAULT_LIMIT = 1000;

	private byte buf[];
	private int count;

	// 下面3个字段是我自己加的
	private long expectedCount;
	private final int limit;
	private boolean overflow;
	
	/**
	 * 
	 */
	public LimitedByteArrayOutputStream() {
		this(DEFAULT_LIMIT);
	}

	/**
	 * 构造方法加了limit设置
	 * 
	 * @param limit
	 */
	public LimitedByteArrayOutputStream(int limit) {
		buf = new byte[1024];
		if (limit < DEFAULT_LIMIT) {
			this.limit = DEFAULT_LIMIT;
		} else {
			this.limit = limit;
		}
	}

	/**
	 * @param minCapacity
	 * @return 需要写入的字节数
	 */
	private int ensureCapacity(int minCapacity) {
		if (count == limit) {
			overflow = true;
			return 0;
		}
		if (minCapacity > limit) {
			overflow = true;
			minCapacity = limit;
		}
		if (minCapacity - buf.length > 0) {
			grow(minCapacity);
		}

		return minCapacity - count;
	}

	private void grow(int minCapacity) {
		int oldCapacity = buf.length;
		int newCapacity = oldCapacity << 1;
		if (newCapacity - minCapacity < 0)
			newCapacity = minCapacity;
		if (newCapacity < 0) {
			if (minCapacity < 0) // overflow
				throw new OutOfMemoryError();
			newCapacity = Integer.MAX_VALUE;
		}

		buf = Arrays.copyOf(buf, newCapacity);
	}

	/**
	 * 加了是否已经溢出的判断
	 * 
	 * @param b
	 *            the data.
	 */
	public synchronized void write(int b) {
		if (!overflow) {
			if (ensureCapacity(count + 1) > 0) {
				buf[count] = (byte) b;
				count += 1;
			}
		}
		expectedCount++;
	}

	/**
	 * 加了是否已经溢出的判断
	 * 
	 * @param b
	 *            the data.
	 * @param off
	 *            the start offset in the data.
	 * @param len
	 *            the number of bytes to write.
	 */
	public synchronized void write(byte b[], int off, int len) {
		if ((off < 0) || (off > b.length) || (len < 0)
				|| ((off + len) - b.length > 0)) {
			throw new IndexOutOfBoundsException();
		}
		if (!overflow) {
			int toCopyLen;
			if ((toCopyLen = ensureCapacity(count + len)) > 0) {
				System.arraycopy(b, off, buf, count, toCopyLen);
				count += toCopyLen;
			}
		}
		expectedCount += len;
	}

	public synchronized void writeTo(OutputStream out) throws IOException {
		out.write(buf, 0, count);
	}

	public synchronized void reset() {
		count = 0;
		overflow = false;
		expectedCount = 0;
	}

	public synchronized byte toByteArray()[] {
		return Arrays.copyOf(buf, count);
	}

	public synchronized int size() {
		return count;
	}

	/**
	 * 实际需要被写入的总字节数
	 * 
	 * @return
	 */
	public synchronized long getExpectedCount() {
		return expectedCount;
	}

	/**
	 * 是否超出限制
	 * 
	 * @return the overflow
	 */
	public synchronized boolean isOverflow() {
		return overflow;
	}

	/**
	 * @param overflowSuffix 如果溢出，后面加上点后缀，比如...
	 * @return
	 */
	public synchronized String toString(String overflowSuffix) {		
		return toString(StandardCharsets.UTF_8, overflowSuffix);
	}

	/**
	 * @param charsetName
	 * @param overflowSuffix 如果溢出，后面加上点后缀，比如...
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public synchronized String toString(String charsetName , String overflowSuffix)
			throws UnsupportedEncodingException {
		return toString(Charset.forName(charsetName), overflowSuffix);
	}
	
	/**
	 * @param charset
	 * @param overflowSuffix 如果溢出，后面加上点后缀，比如...
	 * @return
	 */
	public synchronized String toString(Charset charset, String overflowSuffix) {
		
		String s = new String(buf, 0, count, charset);
		final int len = s.length();
		int i = 0;
		for ( ; i<len; ){
            int c = Character.codePointAt( s , i );
            if (c == 0x0) {
            	break;
            }
            i += Character.charCount(c);
		}
        if (i < len) {        	
        	s = StringUtils.substring(s, 0, i);
        	return s;
        }
		if (overflow ) {
			if (overflowSuffix!= null) {
				s = s + overflowSuffix;
			}
			s = s +  (expectedCount - limit) + "bytes ignored";
		}
		
		return s;
	}

	public void close() throws IOException {
	}
}